package com.cjx.stars.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cjx.stars.common.MyException;
import com.cjx.stars.common.Response;
import com.cjx.stars.common.ResponseUtils;
import com.cjx.stars.constant.FailMessage;
import com.cjx.stars.mapper.TeamMapper;
import com.cjx.stars.mapper.UserTeamMapper;
import com.cjx.stars.pojo.request.TeamCoverUpdateRequest;
import com.cjx.stars.pojo.table.Team;
import com.cjx.stars.pojo.table.User;
import com.cjx.stars.pojo.table.UserTeam;
import com.cjx.stars.pojo.vo.TeamVo;
import com.cjx.stars.pojo.vo.UserVo;
import com.cjx.stars.pojo.request.JoinTeam;
import com.cjx.stars.service.TeamService;
import com.cjx.stars.service.UserService;
import com.cjx.stars.service.UserTeamService;
import com.cjx.stars.utils.FileUtils;
import com.cjx.stars.utils.RedisIdWorker;
import com.cjx.stars.utils.RegexValidationUtils;
import com.cjx.stars.utils.UserHolder;

import lombok.extern.log4j.Log4j2;
import org.apache.poi.ss.formula.functions.T;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.cjx.stars.common.TeamStatus.ENCRYPT_STATUS;
import static com.cjx.stars.common.TeamStatus.PRIVATE_STATUS;
import static com.cjx.stars.constant.FailMessage.*;
import static com.cjx.stars.constant.SystemConstants.*;
import static com.cjx.stars.service.impl.UserServiceImpl.isAdmin;

/**
 * @author cjx
 * @description 针对表【team(队伍)】的数据库操作Service实现
 * @createDate 2024-03-16 17:26:32
 */
@Service
@Log4j2
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService {

    @Resource
    private RedisIdWorker redisIdWorker;
    @Resource
    private TeamMapper teamMapper;
    @Resource
    private UserTeamMapper userTeamMapper;
    @Resource
    private UserTeamService userTeamService;
    @Resource
    private UserService userService;
    @Resource
    private RedissonClient redissonClient;

    @Value("${stars.qiniu.url:null}")
    private String qiniuUrl;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response<?> addTeam(Team team) {
        Integer maxNum = team.getMaxNum();
        //1. 判断人数是否在区间中
        if (maxNum < 0 || maxNum > MAXIMUM_TEAM_SIZE) {
            throw new MyException(ERROR_INCORRECT_FORMAT);
        }
        //2. 判断队伍类型，如果是加密队伍则校验密码格式并加密
        Integer status = team.getStatus();
        String password = team.getPassword();
        if (status == ENCRYPT_STATUS.getCode()) {
            if (!RegexValidationUtils.checkPassword(password)) {
                throw new MyException(ERROR_INCORRECT_FORMAT);
            }
            password = DigestUtil.md5Hex(password);
        }
        team.setPassword(password);
        //3. 添加登录用户id
        UserVo user = UserHolder.getUser();
        if (user == null) {
            throw new MyException(ERROR_THE_USER_IS_NOT_LOGGED_IN);
        }
        team.setUserId(Long.valueOf(user.getUserId()));
        long teamId = insertTeam(team);
        //将自己加入自己的队伍
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(Long.valueOf(user.getUserId()));
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(DateTime.now());
        userTeamMapper.insert(userTeam);
        return ResponseUtils.success();
    }

        public long insertTeam(Team team) {
        log.info("新增队伍");
        //4. 生成队伍id
        long teamId = redisIdWorker.createId(TEAM_KEY_PREFIX);
                team.setId(teamId);
        //5. 插入数据库
        this.save(team);
        return teamId;
    }

    @Override
    public Response<?> getTeamList(String searchText, int status, int currentPage) {
        // 封装分页条件
        Page<Team> teamPage = new Page<>(currentPage, PAGE_SIZE);
//        1. 取队伍状态，做eq条件
        LambdaQueryWrapper<Team> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Team::getStatus, status);
//                - 如果是查询私有队伍，则只允许管理员查询
        try {
            if (status == PRIVATE_STATUS.getCode() && !isAdmin()) {
                throw new MyException(ERROR_INCORRECT_FORMAT);
            }
            if (status == PRIVATE_STATUS.getCode() && isAdmin()) {
                Page<Team> teamList = teamMapper.selectPage(teamPage, wrapper);
                return ResponseUtils.success(teamList);
            }
        } catch (Exception e) {
            throw new MyException(ERROR_THE_USER_IS_NOT_LOGGED_IN);
        }
//        3. 如果是模糊查询，将其关键字添加入查询条件
//        5. 将查询结果封装返回
        return paginateTheTeam(searchText, status, currentPage, -1, new LambdaQueryWrapper<Team>());
    }

    @Override
    public Response<?> getMyCreateTeamList(String searchText, int currentPage) {
        return paginateTheTeam(searchText, -1, currentPage, Long.parseLong(UserHolder.getUser().getUserId()), new LambdaQueryWrapper<Team>());
    }

    @Override
    public Response<?> queryTeam(Long teamId) {
        Team team = getTeamById(teamId);
        if (team == null) {
            throw new MyException(ERROR_THE_TEAM_DOES_NOT_EXIST);
        }
        TeamVo teamVo = BeanUtil.copyProperties(team, TeamVo.class);
        //查询队伍人数
        LambdaQueryWrapper<UserTeam> wrapper = new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getTeamId, Long.valueOf(teamVo.getId()));
        long count = userTeamMapper.selectCount(wrapper);
        teamVo.setHasJoinNum((int) count);
        UserVo user = UserHolder.getUser();
        if (user == null) {
            throw new MyException(ERROR_THE_USER_IS_NOT_LOGGED_IN);
        }
        String userId = user.getUserId();
        wrapper.eq(UserTeam::getUserId, Long.valueOf(userId));
        long userCount = userTeamMapper.selectCount(wrapper);
        //查询队长用户信息
        User byId = userService.getById(teamVo.getUserId());
        teamVo.setLeaderName(byId.getAccount());
        teamVo.setHasJoin(userCount > 0);
        return ResponseUtils.success(teamVo);
    }

    @Override
    public Response<?> joinTeam(JoinTeam joinTeam) {
        Long teamId = joinTeam.getTeamId();
        String password = joinTeam.getPassword();
        UserVo user = UserHolder.getUser();
        if (user == null) {
            throw new MyException(ERROR_THE_USER_IS_NOT_LOGGED_IN);
        }
        String userId = user.getUserId();
        //上锁
        RLock lock = redissonClient.getLock("joinTeam");
        try {
            if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {
                //        1. 判断队伍人数是否已满，已满无法加入
                List<UserTeam> teams = userTeamMapper.selectList(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getTeamId, teamId));
                int teamSize = teams.size();
                //判断当前用户是否已加入该队伍
                for (UserTeam team : teams) {
                    if (team.getUserId().equals(Long.valueOf(userId))) {
                        throw new MyException(DUPLICATE_PARTY_REJOINS_ARE_NOT_PERMITTED);
                    }
                }
                //队伍不存在
                if (teamSize == 0) {
                    throw new MyException(ERROR_THE_TEAM_DOES_NOT_EXIST);
                }
                Team team = teamMapper.selectById(teamId);
                Integer maxNum = team.getMaxNum();
                if (teamSize == maxNum) {
                    throw new MyException(THE_QUEUE_IS_FULL);
                }
//        2. 判断队伍类型，私有队伍不允许加入，加密队伍则密码字段必须拥有
                Integer status = team.getStatus();
                if (status == PRIVATE_STATUS.getCode()) {
                    throw new MyException(PRIVATE_TEAMS_ARE_NOT_ALLOWED_TO_JOIN);
                }
                if (status == ENCRYPT_STATUS.getCode()) {
                    if (StrUtil.isBlank(password)) {
                        throw new MyException(PLEASE_ENTER_YOUR_PASSWORD);
                    }
//        3. 如果密码不为空，带密码id查询数据库，匹配的上，则加入成功
                    String digestPassword = DigestUtil.md5Hex(password);
                    long count = count(new LambdaQueryWrapper<Team>().eq(Team::getId, teamId).eq(Team::getPassword, digestPassword));
//        4. 匹配不上，加入失败，返回密码错误消息、
                    if (count == 0) {
                        throw new MyException(WRONG_PASSWORD);
                    }
                }
//        5. 公开队伍，给队伍-用户关系表添加相关信息
                UserTeam userTeam = new UserTeam();
                userTeam.setTeamId(teamId);
                userTeam.setUserId(Long.valueOf(userId));
                userTeam.setJoinTime(DateTime.now());
                userTeamMapper.insert(userTeam);

            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
//        6. 返回成功信息
        return ResponseUtils.success();

    }

    @Override
    public Response<?> quitTeam(Long teamId) {
//        1. 判断登录用户是否在该队伍中
        UserVo user = UserHolder.getUser();
        Long userId = Long.valueOf(user.getUserId());
        LambdaQueryWrapper<UserTeam> wrapperUserTeam = new LambdaQueryWrapper<UserTeam>()
                .eq(UserTeam::getTeamId, teamId).eq(UserTeam::getUserId, userId);
        long count = userTeamService.count();
        if (count == 0) {
            throw new MyException(USER_NOT_JOIN_THE_TEAM);
        }
//        2. 如果此队伍只有一人，此调用解散队伍接口
        int teamCount = userTeamService.getTeamCount(teamId);
        if (teamCount == 1) {
            deleteTeam(teamId);
        }
//        3. 判断此用户是否为队长
        LambdaQueryWrapper<Team> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Team::getId, teamId).eq(Team::getUserId, userId);
        long isUser = count(wrapper);
//     - 是队长执行修改队伍队长id功能，然后删除该用户信息
        if (isUser > 0) {
            updateCaptain(teamId, userId);
        }
//     - 不是队长，直接退出该队伍即可
        userTeamService.remove(wrapperUserTeam);
        return ResponseUtils.success();

    }

    /**
     * 修改队长功能
     *
     * @param teamId 队伍Id
     * @param userId 用户Id
     */
    private void updateCaptain(Long teamId, Long userId) {
        LambdaQueryWrapper<UserTeam> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserTeam::getTeamId, teamId).notIn(UserTeam::getUserId, userId);
        UserTeam userTeam = userTeamService.getOne(wrapper);
        //查询目前该队伍最老的用户
        Long captainId = userTeam.getUserId();
        update(new UpdateWrapper<Team>().eq("id", teamId).set("user_id", captainId));
    }

    @Override
    public Response<?> deleteTeam(Long teamId) {
        Long userId = Long.valueOf(UserHolder.getUser().getUserId());
        isCaptain(teamId, userId);
        //删除用户队伍表的相关关系
        userTeamService.remove(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getTeamId, teamId));
        //删除队伍信息
        remove(new LambdaQueryWrapper<Team>().eq(Team::getId, teamId));
        return ResponseUtils.success();
    }

    private Response<?> isCaptain(Long teamId, Long userId) {
        long count = count(new LambdaQueryWrapper<Team>().eq(Team::getId, teamId).eq(Team::getUserId, userId));
        //不是该队伍的队长发送的解散队伍请求，返回错误信息
        if (count == 0) {
            throw new MyException(ERROR_INSUFFICIENT_PERMISSIONS);
        }
        return null;
    }

    @Override
    public Response<?> getMember(Long teamId) {
        //1.查询除自己外的成员id
        List<Long> userIdsByTeamId = userTeamService.getUserIdsByTeamId(teamId);
        //1.1 剔除登录用户id
        userIdsByTeamId.remove(Long.valueOf(UserHolder.getUser().getUserId()));
        if (userIdsByTeamId.isEmpty()) {
            return ResponseUtils.success();
        }
        //2.根据成员id批量查询用户信息
        List<User> users = userService.listByIds(userIdsByTeamId);

        //3.将用户信息转换成安全用户信息后返回
        List<UserVo> userVos = users.stream().map(user -> BeanUtil.copyProperties(user, UserVo.class)).collect(Collectors.toList());
        return ResponseUtils.success(userVos);

    }

    @Override
    public Response<?> kickUser(Long teamId, Long userId) {
        UserVo user = UserHolder.getUser();
        //判断当前登录用户是否为当前队伍队长
        isCaptain(teamId, Long.valueOf(user.getUserId()));
        //是队长则从用户队伍关系表删除该用户信息
        userTeamService.removeByTeamIdAndUserId(teamId, userId);
        return ResponseUtils.success();
    }

    @Override
    public Response<?> teamUpdate(Team team) {
        //判断当前用户是否为该队伍队长
        UserVo user = UserHolder.getUser();
        if (!team.getUserId().equals(Long.valueOf(user.getUserId()))) {
            throw new MyException(ERROR_INSUFFICIENT_PERMISSIONS);
        }
        //如果有密码给密码加密
        if (!StrUtil.isBlank(team.getPassword())) {
            team.setPassword(DigestUtil.md5Hex(team.getPassword()));
        }
        update(new UpdateWrapper<Team>().eq("id", team.getId()).set("name", team.getName())
                .set("description", team.getDescription()).set("expire_time", team.getExpireTime())
                .set("status", team.getStatus()).set("password", team.getPassword()));
        return ResponseUtils.success();
    }

    @Override
    public Response<?> myJoinTeam(String searchText, int currentPage) {
        //分页查询我加入的队伍的id
        Page<UserTeam> page = new Page<>(currentPage, PAGE_SIZE);
        LambdaQueryWrapper<UserTeam> userTeamWrapper = new LambdaQueryWrapper<>();
        UserVo user = UserHolder.getUser();
        userTeamWrapper.eq(UserTeam::getUserId, Long.valueOf(user.getUserId()));
        Page<UserTeam> userTeamPage = userTeamMapper.selectPage(page, userTeamWrapper);
        List<UserTeam> records = userTeamPage.getRecords();
        if (records == null || records.isEmpty()) {
            return ResponseUtils.success(userTeamPage);
        }
        List<Long> teamId = records.stream().map(UserTeam::getTeamId).toList();
        LambdaQueryWrapper<Team> teamWrapper = new LambdaQueryWrapper<Team>().in(Team::getId, teamId);
        //根据队伍id查询相应的队伍信息
        //返回队伍信息
        return paginateTheTeam(searchText, -1, currentPage, -1, teamWrapper);
    }

    @Override
    public Response<?> myJoinTeams() {
        //1.获取登录用户
        UserVo user = UserHolder.getUser();
        //2.查找已加入的队伍id
        List<UserTeam> userTeams = userTeamMapper.selectList(new LambdaQueryWrapper<UserTeam>()
                .eq(UserTeam::getUserId, Long.valueOf(user.getUserId())));
        List<Long> teamIds = userTeams.stream().map(UserTeam::getTeamId).toList();
        //3.根据id查询队伍
        List<Team> teams = teamMapper.selectBatchIds(teamIds);
        List<TeamVo> teamVos = teams.stream().map(team -> BeanUtil.copyProperties(team, TeamVo.class)).toList();
        return ResponseUtils.success(teamVos);

    }

    @Override
    public void changeCoverImage(TeamCoverUpdateRequest teamCoverUpdateRequest) {
        String userId = UserHolder.getUser().getUserId();
        MultipartFile image = teamCoverUpdateRequest.getFile();
        if (image == null) {
            throw new MyException(ERROR_PARAMETER_IS_BLACK);
        }
        Long teamId = teamCoverUpdateRequest.getId();
        if (teamId == null) {
            throw new MyException(ERROR_PARAMETER_IS_BLACK);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new MyException(ERROR_THE_TEAM_DOES_NOT_EXIST);
        }
        if (!team.getUserId().equals(Long.valueOf(userId))) {
            throw new MyException(ERROR_INSUFFICIENT_PERMISSIONS);
        }
        String fileName = FileUtils.uploadFile(image);
        Team temp = new Team();
        temp.setId(team.getId());
        temp.setCoverImage(qiniuUrl + fileName);
        this.updateById(temp);
    }

    /**
     * 查询队伍已加入人数
     *
     * @param teamId 队伍id
     * @return 已加入人数
     */
    private int selectTeamCount(String teamId) {
        long count = userTeamMapper.selectCount(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getTeamId, teamId));
        return (int) count;
    }

    /**
     * 分页条件查询队伍
     *
     * @param searchText  搜索词
     * @param currentPage 分页数据
     * @param status      状态
     * @return 返回
     */
    private Response<Page<TeamVo>> paginateTheTeam(String searchText, int status, int currentPage, long userId, LambdaQueryWrapper<Team> wrapper) {
        Page<Team> teamPage = new Page<>(currentPage, PAGE_SIZE);
//        LambdaQueryWrapper<Team> wrapper = new LambdaQueryWrapper<>();
        if (status != -1) {
            wrapper.eq(Team::getStatus, status);
        }
        if (userId != -1) {
            wrapper.eq(Team::getUserId, userId);
        }

        if (!StrUtil.isBlank(searchText)) {
            wrapper.like(Team::getName, searchText);
        }
        // 不查询过期队伍
        wrapper.gt(Team::getExpireTime, LocalDateTime.now());
        Page<Team> teamList = teamMapper.selectPage(teamPage, wrapper);
        List<Team> records = teamList.getRecords();
        List<Long> teamIds = records.stream().map(Team::getId).collect(Collectors.toList());
        //查询每个队伍的用户头像
        List<List<String>> joinedUserAvatars = getJoinedUserAvatars(teamIds);
        //获取登录账号是否加入查询队伍的信息
        UserVo user = UserHolder.getUser();
        boolean[] isJoin = new boolean[teamIds.size()];
        if (user != null) {
            isJoin = userTeamService.isJoin(user.getUserId(), teamIds);
        }
//        userId
        List<TeamVo> collect = records.stream().map((team -> BeanUtil.copyProperties(team, TeamVo.class))).toList();
        //给每个队伍插入加入用户头像
        if (joinedUserAvatars != null) {
            for (int i = 0; i < collect.size(); i++) {
                TeamVo teamVo = collect.get(i);
                if (isJoin[i]) {
                    teamVo.setHasJoin(true);
                }
                teamVo.setJoinedUserAvatars(joinedUserAvatars.get(i));
            }

        }
        Page<TeamVo> safeTeamPage = new Page<>(currentPage, PAGE_SIZE);
        safeTeamPage.setRecords(collect);
        return ResponseUtils.success(safeTeamPage);
    }

    /**
     * 查询队伍列表中已加入用户的头像
     *
     * @param teamIds 队伍id
     * @return 已加入用户的头像
     */
    public List<List<String>> getJoinedUserAvatars(List<Long> teamIds) {
        List<List<String>> joinedUserAvatars = new ArrayList<>();
        for (Long teamId : teamIds) {
            //查询一个队伍的所有用户id
            List<Long> userIds = userTeamService.getUserIdsByTeamId(teamId);
            //根据用户id查询所有头像
            List<String> userAvatars = userService.getUserAvatarsByUserIds(userIds);
            if (userAvatars == null) {
                return null;
            }
            joinedUserAvatars.add(new ArrayList<>(userAvatars));
        }
        return joinedUserAvatars;
    }

    /**
     * 根据id获取队伍信息
     * @param teamId 队伍id
     * @return 队伍信息
     */
    public Team getTeamById(long teamId){
        return getById(teamId);
    }

}




